﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;

namespace FITClub.Episode1.Controller.AI
{
    /// <summary>
    /// Element of waypoint graph
    /// </summary>
    public class WPNode
    {
        private List<WPNode> siblings;

        /// <summary>
        /// How should character preferably react.
        /// </summary>
        [XmlAttribute]
        public NodeBahaviour Behaviour { get; set; }

        /// <summary>
        /// Unique identifier
        /// </summary>
        [XmlAttribute]
#if WINDOWS
        [ReadOnly(true)]
#endif
        public string Name { get; set; }

        /// <summary>
        /// Position in the level.
        /// </summary>
        public Vector2 Position { get; set; }

        [XmlIgnore]
        public TraversalState Visited;

        [XmlIgnore]
        public WPNode Predcessor;

        [XmlIgnore]
        public WPNode DijkstraPredcessor;

        [XmlIgnore]
        public int Depth;

        [XmlIgnore]
        public int SiblingsCount { get { return this.siblings.Count; } }

        /// <summary>
        /// Connected nodes with direct path.
        /// </summary>
        [XmlIgnore]
        public IList<WPNode> Siblings { get { return this.siblings; } }

        [XmlIgnore]
        public float Distance;

        ///// <summary>
        ///// Incidentions
        ///// </summary>
        //[XmlIgnore]
        //public Incidention[] Incidentions { get; set; }

        public WPNode()
        {
            this.siblings = new List<WPNode>();
        }

        /// <summary>
        /// Create direct path from this node to other.
        /// </summary>
        /// <param name="other">Node tobe adden between siblings.</param>
        public void ConnectTo(WPNode other)
        {
            if (this != other && this.siblings.Contains(other) == false)
            {
                this.siblings.Add(other);
            }
        }

        public override string ToString()
        {
            return string.Format("{0} [{1}; {2}]", this.Name, this.Position.X, this.Position.Y);
        }

        public void Disconnect(WPNode node)
        {
            this.siblings.Remove(node);
        }

        /// <summary>
        /// Create two-way connection between nodes.
        /// </summary>
        /// <param name="node1"></param>
        /// <param name="node2"></param>
        public static void Connect(WPNode node1, WPNode node2)
        {
            node1.ConnectTo(node2);
            node2.ConnectTo(node1);
        }
    }

    public struct Incidention
    {
        public WPNode start;
        public WPNode end;
        public float distance;

        public Incidention(WPNode start, WPNode end, Vector2 point)
        {
            distance = Waypoints.LinePointDistance(start.Position, end.Position, point);
            this.end = end;
            this.start = start;
        }
    }

    /// <summary>
    /// Serializable wrapper for WPNode
    /// </summary>
    public class SerializableWPNode
    {
        public WPNode Node { get; set; }

        [XmlArray]
        public List<string> SiblingNames { get; set; }

        public SerializableWPNode()
        {
        }

        public SerializableWPNode(WPNode node)
        {
            this.Node = node;
            this.SiblingNames = (from itm in node.Siblings select itm.Name).ToList();
        }
    }

    public class WPNodeDistanceComparer : IComparer<WPNode>
    {

        public int Compare(WPNode x, WPNode y)
        {
            return x.Distance.CompareTo(y.Distance);
        }
    }
}